package threadcoreknowledge.threadobjectclasscommonmethods;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

/**
 * 手写生产者消费者设计模式
 * 用wait/notify实现生产者、消费者
 * <p>
 * 生产者消费者设计模式 是wait和notify的一大用途.
 * 如下的代码中, 用wait和notify的替换, 实现了生产者消费者模式.
 * <p>
 * put方法用于生产者. 该方法的逻辑是首先判断容器的容量是否达到了仓库的最大容量,如果是的, 那么就调用wait, 让消费者去消费.
 * 如果仓库还没满, 那么就进行生产, 生产完了之后, 就调用notify去唤醒消费者线程.
 * <p>
 * take方法的逻辑是首先判断容器中的产品容量是否为0 ,如果是0 , 那么就调用wait进入等待状态, 等待生产者唤醒.
 * 如果容器中 的产品容量不是0 , 那么就消费产品,并且唤醒生产者, 去生产产品.
 * <p>
 * 创建生产者和消费者两个线程, 分别去调用put和take方法. 去生产和消费.
 * ————————————————
 * 原文链接：https://blog.csdn.net/qq_33229669/article/details/108298131
 */
public class ProducerConsumerModel {

    public static void main(String[] args) {
        EventStorage eventStorage = new EventStorage();
        Producer producer = new Producer(eventStorage);
        Consumer consumer = new Consumer(eventStorage);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

/**
 * 生产者
 */
class Producer implements Runnable {
    private EventStorage storage;

    public Producer(EventStorage storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.put();
        }
    }
}

/**
 * 消费者
 */
class Consumer implements Runnable {
    private EventStorage storage;

    public Consumer(EventStorage storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.take();
        }
    }
}

class EventStorage {
    private int maxSize;

    //使用LinkedList的原因是有拿出集合中的第一个元素, 并且删除该元素的方法
    private LinkedList<Date> storage;

    public EventStorage() {

        //仓库的最大容量为10
        maxSize = 10;
        storage = new LinkedList<>();
    }

    /**
     * put方法的作用是往队列中放东西,并且放了东西后, 要通知消费者,进行消费
     * 仓库满了之后, 不能再放入东西 ,并且进入wait状态
     * 为了保证线程安全, 加上synchronized锁
     */
    public synchronized void put() {
        // 当list的size等于maxSize，说明队列满了，生产者阻塞住
        while (storage.size() == maxSize) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //仓库没满, 生产产品, 并且, 唤醒消费者
        storage.add(new Date());
        System.out.println("仓库里有了 " + storage.size() + " 个产品");
        // 生产者生产之后唤醒消费者继续消费
        notify();
    }

    /**
     * 消费者的take方法
     */
    public synchronized void take() {

        //首先判断容器中, 是否有元素, 如果为空,那么调用wait方法, 等待生产者生产线程
        while (storage.size() == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 消费者被唤醒，继续消费数据
        System.out.println(Thread.currentThread().getName() + " 拿到了" + storage.poll() + "，现在仓库还剩下: " + storage.size());
        // 唤醒生产者继续生产，每消费完一个数据，队列都会空出一个位置，此时可以唤醒生产者
        notify();
    }
}
